package written;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 我们提供了一个类：
 * <p>
 * public class Foo {
 *   public void first() { print("first"); }
 *   public void second() { print("second"); }
 *   public void third() { print("third"); }
 * }
 * 三个不同的线程将会共用一个 Foo 实例。
 * <p>
 * 线程 A 将会调用 first() 方法
 * 线程 B 将会调用 second() 方法
 * 线程 C 将会调用 third() 方法
 * 请设计修改程序，以确保 second() 方法在 first() 方法之后被执行，third() 方法在 second() 方法之后被执行。
 *
 * @author TANGYE
 * @date 2021-01-10 17:51
 **/
public class PrintByOrder3 {

    static Lock lock = new ReentrantLock();
    private static int num = 1;

    private static int MAX = 50;

    private static class Foo {

        private Condition cd1;
        private Condition cd2;
        private Condition cd3;

        public Foo(Condition cd1, Condition cd2, Condition cd3) {
            this.cd1 = cd1;
            this.cd2 = cd2;
            this.cd3 = cd3;
        }

        public void first() {
            try {
                lock.lock();
                while (num < 100) {
                    if (num % 3 != 1) {
                        cd1.await();
                    }
                    System.out.println("first");
                    num++;
                    cd2.signal();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void second() {
            try {
                lock.lock();
                while (num < 100) {
                    if (num % 3 != 2) {
                        cd2.await();
                    }
                    System.out.println("second");
                    num++;
                    cd3.signal();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void third() {
            try {
                lock.lock();
                while (num < 100) {
                    if (num % 3 != 0) {
                        cd3.await();
                    }
                    System.out.println("third");
                    num++;
                    cd1.signal();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    private static volatile int count = 0;

    private static class Foo1 {
        public void first() {
            System.out.println("first");
            count++;
        }

        public void second() {
            while (count != 1) {

            }
            System.out.println("second");
            count++;
        }

        public void third() {
            while (count != 2) {

            }
            System.out.println("third");
            count++;
        }
    }

    private static Semaphore semaphore1 = new Semaphore(0);
    private static Semaphore semaphore2 = new Semaphore(0);
    private static Semaphore semaphore3 = new Semaphore(1);

    private static class Foo2 {
        public void first() {
            while (num++ < MAX) {
                try {
                    semaphore3.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("first");
                semaphore1.release();
            }

        }

        public void second() {
            while (num++ < MAX) {
                try {
                    semaphore1.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("second");
                semaphore2.release();
            }
        }

        public void third() {
            while (num++ < MAX) {
                try {
                    semaphore2.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("third");
                semaphore3.release();
            }
        }
    }

    public static void main(String[] args) {

        Foo foo = new Foo(lock.newCondition(), lock.newCondition(), lock.newCondition());
        Foo1 foo1 = new Foo1();
        Foo2 foo2 = new Foo2();
        Thread t1 = new Thread(() -> foo2.first());

        Thread t2 = new Thread(() -> foo2.second());

        Thread t3 = new Thread(() -> foo2.third());

        t1.start();
        t2.start();
        t3.start();
    }
}
